#include "ring_buffer.h"

static inline uint8_t is_ring_buffer_full(ring_buffer_t *ring);
static inline uint8_t is_ring_buffer_empty(ring_buffer_t *ring);
static inline ring_buffer_size_t get_ring_buffer_len(ring_buffer_t *ring);

void ring_buffer_init(ring_buffer_t *ring)
{
    /* check if pointer NULL */
    assert(ring != NULL);

    /* init ring buffer struct */
    ring->head = 0;
    ring->tail = 0;
}

int ring_buffer_enqueue(ring_buffer_t *ring, ring_buffer_data_t data)
{
    if(is_ring_buffer_full(ring)) {
        /* buffer is full, so remove tail to enqueue new data */
        ring->tail = (ring->tail + 1) % RING_BUFFER_SIZE;
#if RING_BUFFER_DEBUG
        ring_buffer_print("\nremove tail cause full!\n");
#endif /* RING_BUFFER_DEBUG */
    }

    /* enqueue data into head */
    ring->buffer[ring->head] = data;
    /* move head index forward */
    ring->head = (ring->head + 1) % RING_BUFFER_SIZE;

    return (int)1;
}

int ring_buffer_enqueue_arr(ring_buffer_t *ring, const ring_buffer_data_t *arr, ring_buffer_size_t len)
{
    if(len >= RING_BUFFER_SIZE) {
#if RING_BUFFER_DEBUG
        ring_buffer_print("\narr len too long!\n");
#endif /* RING_BUFFER_DEBUG */
        return (int)RING_BUFFER_OVERFLOW;
    }

    ring_buffer_size_t cnt = 0;
    for(; cnt < len; cnt++) {
        ring_buffer_enqueue(ring, arr[cnt]);
    }
    return (int)cnt;
}

int ring_buffer_dequeue(ring_buffer_t *ring, ring_buffer_data_t *data)
{
    if(is_ring_buffer_empty(ring)) {
#if RING_BUFFER_DEBUG
        ring_buffer_print("\nring buffer empty!\n");
#endif /* RING_BUFFER_DEBUG */
        return (int)RING_BUFFER_EMPTY;
    }

    /* dequeue tail data */
    *data = ring->buffer[ring->tail];
    /* move tail index forward */
    ring->tail = (ring->tail + 1) % RING_BUFFER_SIZE;

    return (int)1;
}

int ring_buffer_dequeue_arr(ring_buffer_t *ring, ring_buffer_data_t *data, ring_buffer_size_t len)
{
    if(len > get_ring_buffer_len(ring)) {
#if RING_BUFFER_DEBUG
        ring_buffer_print("\nbuffer no enough!\n");
#endif /* RING_BUFFER_DEBUG */
        return (int)RING_BUFFER_NOENOUGH;
    }

    ring_buffer_size_t cnt = 0;
    for(; cnt < len; cnt++) {
        if(ring_buffer_dequeue(ring, data++) != 1) {
            /* return if buffer used up */
            return (int)cnt;
        }
    }
    return (int)cnt;
}

#if RING_BUFFER_DEBUG
void ring_buffer_debug(ring_buffer_t *ring)
{
    ring_buffer_print("\n================\n");
    ring_buffer_print("ring buffer ctx:\n");
    ring_buffer_print("----------------\n");
    if(is_ring_buffer_empty(ring)) {
        ring_buffer_print("NULL\n");
    }
    else {
        ring_buffer_print("(head) -> ");
        if(ring->head > ring->tail) {
            for(long int tmp = ring->head - 1; tmp >= (long int)ring->tail; tmp--) {
                ring_buffer_print(RING_BUFFER_FORMAT " -> ", ring->buffer[tmp]);
            }
        }
        else {
            for(long int tmp = ring->head - 1; tmp >= 0; tmp--) {
                ring_buffer_print(RING_BUFFER_FORMAT " -> ", ring->buffer[tmp]);
            }
            for(long int tmp = RING_BUFFER_SIZE - 1; tmp >= (long int)ring->tail; tmp--) {
                ring_buffer_print(RING_BUFFER_FORMAT " -> ", ring->buffer[tmp]);
            }
        }
        ring_buffer_print("(end)\n");
    }
    ring_buffer_print("================\n");
}
#endif /* RING_BUFFER_DEBUG */

static inline uint8_t is_ring_buffer_full(ring_buffer_t *ring)
{
    return ((ring->head + 1) % RING_BUFFER_SIZE == ring->tail);
}

static inline uint8_t is_ring_buffer_empty(ring_buffer_t *ring)
{
    return (ring->head == ring->tail);
}

static inline ring_buffer_size_t get_ring_buffer_len(ring_buffer_t *ring)
{
    return ((ring->head - ring->tail) % RING_BUFFER_SIZE);
}